#pragma once

#include "VariableManager.hpp"
#include <limits>

struct BrowseableFileType
{
	// fileType can be NULL to get a general file open dialog with no filters.
	static bool ShowOpenDialog(string &outFilePath, BrowseableFileType *fileType);
	static bool ShowSaveDialog(string &outFilePath, BrowseableFileType *fileType);

	BrowseableFileType() : LastFilterIndex(0) { }

	string Title;
	string Filter;
	string DefaultExtension;

	/// Changed by ShowOpenDialog
	string LastFilePath;
	uint LastFilterIndex;
};

class EnumType
{
public:
	virtual ~EnumType() { }

	virtual uint GetCount() const = 0;
	virtual void GetName(string &out, uint index) const = 0;
	virtual uint GetValue(uint index) const = 0;

	bool NameToValue(uint &outValue, const string &name) const;
	bool ValueToName(string &outName, uint value) const;
};

class StaticEnumType : public EnumType
{
public:
	StaticEnumType() : m_Count(0), m_Names(NULL), m_Values(NULL) { }
	// values == NULL means consecutive values 0, 1, 2, ...
	StaticEnumType(uint count, const char * const *names, const uint *values = NULL) : m_Count(count), m_Names(names), m_Values(values) { }

	void Set(uint count, const char * const *names, const uint *values = NULL)
	{
		m_Count = count;
		m_Names = names;
		m_Values = values;
	}

	virtual uint GetCount() const { return m_Count; }
	virtual void GetName(string &out, uint index) const { out = m_Names[index]; }
	virtual uint GetValue(uint index) const { return m_Values ? m_Values[index] : index; }

private:
	uint m_Count;
	const char * const *m_Names;
	const uint *m_Values; // Can be NULL.
};

class DynamicEnumType : public EnumType
{
public:
	struct Item
	{
		std::string Name;
		uint Value;
	};

	std::vector<Item> m_Items;

	virtual uint GetCount() const { return m_Items.size(); }
	virtual void GetName(string &out, uint index) const { out = m_Items[index].Name; }
	virtual uint GetValue(uint index) const { return m_Items[index].Value; }
};

class CharVariable : public RawVariable<char>
{
public:
	CharVariable(const string &name, uint id, uint flags = 0) : RawVariable<char>(name, id, flags) { }

	virtual const char * GetTypeName() { return "char"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tokenizer);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tokWriter);
	virtual void LoadFromString(IParamObject &obj, const string &s);
	virtual void SaveToString(const IParamObject &obj, string &out);
};

class BoolVariable : public RawVariable<bool>
{
public:
	BoolVariable(const string &name, uint id, uint flags = 0) : RawVariable<bool>(name, id, flags) { }

	virtual const char * GetTypeName() { return "bool"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tokenizer);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tokWriter);
	virtual void LoadFromString(IParamObject &obj, const string &s);
	virtual void SaveToString(const IParamObject &obj, string &out);
};

class StlStringVariable : public VariableT<string>
{
public:
	StlStringVariable(const string &name, uint id, uint flags = 0) : VariableT<string>(name, id, flags) { }

	virtual const char * GetTypeName() { return "std::string"; }
	virtual void LoadBinary(IParamObject &obj, common::Stream &stream);
	virtual void SaveBinary(const IParamObject &obj, common::Stream &stream);
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tokenizer);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tokWriter);
	virtual void LoadFromString(IParamObject &obj, const string &s);
	virtual void SaveToString(const IParamObject &obj, string &out);
	virtual void ToShortString(const IParamObject &obj, string &out);
};

class FilePathVariable : public StlStringVariable
{
public:
	// Can be NULL. Lifetime of the referenced value must be managed externally.
	BrowseableFileType *FileType;

	FilePathVariable(const string &name, uint id, uint flags = 0) : StlStringVariable(name, id, flags), FileType(NULL) { }
	FilePathVariable(const string &name, uint id, uint flags, BrowseableFileType *fileType) : StlStringVariable(name, id, flags), FileType(fileType) { }
	virtual const char * GetTypeName() { return "FilePath"; }
};

class DirPathVariable : public StlStringVariable
{
public:
	static bool ShowDirDialog(string &inoutDir, const string &title);

	DirPathVariable(const string &name, uint id, uint flags = 0) : StlStringVariable(name, id, flags) { }
	virtual const char * GetTypeName() { return "DirPath"; }
};

template <typename T>
class NumericVariable : public RawVariable<T>
{
public:
	NumericVariable(const string &name, uint id, uint flags = 0) : RawVariable<T>(name, id, flags) { }
	virtual void SaveText(const IParamObject &obj, TokenWriter &tokWriter);
	virtual void LoadFromString(IParamObject &obj, const string &s)
	{
		T v; MustStrToSth<T>(&v, s); SetValue(obj, v);
	}
	virtual void SaveToString(const IParamObject &obj, string &out)
	{
		T v; GetValue(obj, v); SthToStr<T>(&out, v);
	}
};

template <typename T>
class IntegerVariable : public NumericVariable<T>
{
public:
	T MinValue, MaxValue, StepValue;

	IntegerVariable(const string &name, uint id, uint flags = 0)
	: NumericVariable<T>(name, id, flags)
	, MinValue(std::numeric_limits<T>::min())
	, MaxValue(std::numeric_limits<T>::max())
	, StepValue((T)1)
	{
	}

	IntegerVariable(const string &name, uint id, uint flags, T minValue, T maxValue, T stepValue)
	: NumericVariable<T>(name, id, flags)
	, MinValue(minValue)
	, MaxValue(maxValue)
	, StepValue(stepValue)
	{
	}
};

template <typename T>
class UintVariable : public IntegerVariable<T>
{
public:
	UintVariable(const string &name, uint id, uint flags = 0) : IntegerVariable<T>(name, id, flags) { }
	UintVariable(const string &name, uint id, uint flags, T minValue, T maxValue, T stepValue) : IntegerVariable<T>(name, id, flags, minValue, maxValue, stepValue) { }

	virtual void LoadText(IParamObject &obj, common::Tokenizer &tokenizer);
};

template <typename T>
class IntVariable : public IntegerVariable<T>
{
public:
	IntVariable(const string &name, uint id, uint flags = 0) : IntegerVariable<T>(name, id, flags) { }
	IntVariable(const string &name, uint id, uint flags, T minValue, T maxValue, T stepValue) : IntegerVariable<T>(name, id, flags, minValue, maxValue, stepValue) { }

	virtual void LoadText(IParamObject &obj, common::Tokenizer &tokenizer);
};

typedef UintVariable<uint1> Uint8Variable;
typedef UintVariable<uint2> Uint16Variable;
typedef UintVariable<uint4> Uint32Variable;
typedef UintVariable<uint8> Uint64Variable;

typedef IntVariable<int1> Int8Variable;
typedef IntVariable<int2> Int16Variable;
typedef IntVariable<int4> Int32Variable;
typedef IntVariable<int8> Int64Variable;

template <typename T>
class RealVariable : public NumericVariable<T>
{
public:
	T MinValue, MaxValue;
	T StepValue; // 0 means default.
	uint Precision; // 0 means default.

	RealVariable(const string &name, uint id, uint flags = 0)
	: NumericVariable<T>(name, id, flags)
	, MinValue(-std::numeric_limits<T>::max())
	, MaxValue( std::numeric_limits<T>::max())
	, StepValue(T())
	, Precision(0)
	{
	}

	RealVariable(const string &name, uint id, uint flags, T minValue, T maxValue, T stepValue, uint precision)
	: NumericVariable<T>(name, id, flags)
	, MinValue(minValue)
	, MaxValue(maxValue)
	, StepValue(stepValue)
	, Precision(precision)
	{
	}

	virtual void LoadText(IParamObject &obj, common::Tokenizer &tokenizer);
};

typedef RealVariable<float> FloatVariable;
typedef RealVariable<double> DoubleVariable;

class EnumVariable : public Uint32Variable
{
public:
	EnumType *m_EnumType;

	EnumVariable(const string &name, uint id, uint flags = 0) : Uint32Variable(name, id, flags), m_EnumType(NULL) { }
	EnumVariable(const string &name, uint id, uint flags, EnumType *enumType) : Uint32Variable(name, id, flags), m_EnumType(enumType) { }
	
	virtual const char * GetTypeName() { return "enum"; }
	virtual void LoadText(IParamObject &obj, common::Tokenizer &tokenizer);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tokenizer);
	virtual void LoadFromString(IParamObject &obj, const string &s);
	virtual void SaveToString(const IParamObject &obj, string &out);
};

class TimeVariable : public RawVariable<GameTime>
{
public:
	static void TimeToStr(string &out, GameTime v);
	static bool StrToTime(GameTime &out, const string &s);

	TimeVariable(const string &name, uint id, uint flags = 0) : RawVariable<GameTime>(name, id, flags) { }

	virtual const char * GetTypeName() { return "time"; }

	virtual void LoadText(IParamObject &obj, common::Tokenizer &tokenizer);
	virtual void SaveText(const IParamObject &obj, TokenWriter &tokenizer);
	virtual void LoadFromString(IParamObject &obj, const string &s);
	virtual void SaveToString(const IParamObject &obj, string &out);
};


#include "VariableTypes.inl"
